﻿#region Header
/*
 * name：Authorization
 * author：Kain
 * date：11/25/2010 11:37:32 AM
 *********************************************
 * History:
 * $Header$
 * $History$
 */
#endregion

#region imported namespace
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Principal;
using System.Threading;
using Cherry.Agile.Core;
using Microsoft.Practices.Unity;
using System.Diagnostics;
#endregion

namespace Cherry.Agile.Security {
    /// <summary>
    /// 安全验证管理
    /// </summary>
    public static class Authorization {

        static IAuthorizationProvider Provider {
            get {
                if (Container.Current.IsRegistered<IAuthorizationProvider>())
                    return Container.Current.Resolve<IAuthorizationProvider>();
                else
                    return new EmptyAuthorizationProvider();
            }

        }
        static ICacheStorage _storage;
        static ITokenProvider _tokenProvider;

        static Authorization() {
            _tokenProvider = GetTokenProvider();
            _storage = GetCacheStorage(_tokenProvider);
        }

        /// <summary>
        /// 验证用户
        /// </summary>
        /// <param name="ticket">用户唯一身份标识</param>
        /// <param name="createPersistent">是否持久保存</param>
        /// <returns>用户信息</returns>
        public static User Authorize(ITicket ticket, bool createPersistent) {
            var user = Provider.Authorize(ticket);
            if (user != null) {
                Trace.TraceInformation("Authorization\tauthorize user:", user.Identity);
                CreatePrincial(user, createPersistent);
                return user;
            }
            return new User();
        }

        /// <summary>
        /// 注销
        /// </summary>
        public static void SignOut() {
            User = null;
            Trace.TraceInformation("Authorization\tuser signout:" + System.Threading.Thread.CurrentPrincipal.Identity.Name);
            _tokenProvider.Clear();

        }

        ///// <summary>
        ///// 获取用户
        ///// </summary>
        ///// <returns></returns>
        private static User GetUser() {
            var token = _tokenProvider.GetCurrentToken();
            if (token != null) {
                var user = Provider.GetUser(token.Value);
                if (user != null)
                    Trace.TraceInformation("Authorization\tresolved user:" + user.Identity);
                else
                    Trace.TraceInformation("Authorization\tresolved user is null");
                return user;
            }
            Trace.TraceWarning("Authorization\tresolved user is null");
            return null;
        }

        /// <summary>
        /// 当前线程上线文中绑定的用户信息
        /// </summary>
        public static User User {
            get {
                var p = Thread.CurrentPrincipal;
                if (p.Identity.IsAuthenticated) {
                    var cache = (User)_storage[AuthorizationDataType.User.ToString()];
                    if (cache == null) {
                        Trace.TraceInformation("Authorization\tload user information from physical storage");
                        cache = GetUser();
                        if (cache != null) {
                            _storage[AuthorizationDataType.User.ToString()] = cache;
                        }
                    } else
                        return cache;
                }

                return _storage[AuthorizationDataType.User.ToString()] as User;
            }
            set {
                var old = _storage[AuthorizationDataType.User.ToString()] as User;

                _storage[AuthorizationDataType.User.ToString()] = value;

                if (old != null && value != null && value.ID != old.ID 
                    && System.Web.HttpContext.Current != null) {
                    Logging.LoggingManager.GetLogger("webapp").Warn(string.Format(
                        "reset user from {0} to {1} by {2}",
                        old.ID,value.ID,System.Web.HttpContext.Current.Request.Url.AbsoluteUri)
                        );
                }
                Trace.TraceInformation("Authorization\tcache user information");
            }
        }

        /// <summary>
        /// 用户身份信息存储槽
        /// </summary>
        public static ICacheStorage Store {
            get { return _storage; }
        }

        /// <summary>
        /// 判断用户是否拥有指定的操作权限
        /// </summary>
        /// <param name="permission">操作权限</param>
        /// <param name="throwError"></param>
        /// <returns>是否拥有指定权限</returns>
        public static bool HasPermission(string permission, bool throwError = false) {
            if (!System.Threading.Thread.CurrentPrincipal.Identity.IsAuthenticated)
                return false;
            var gp = User;
            if (gp == null)
                return false;
            var q = from r in gp.Roles
                    from p in r.Permissions
                    where string.Compare(p.Code, permission, true) == 0
                    select p;
            var ok = q.Count() > 0;
            if (!ok && throwError)
                throw new SecurityException("permission error");
            return ok;
        }

        /// <summary>
        /// 判断一个用户是否属于一个角色
        /// </summary>
        /// <param name="role">角色名称</param>
        /// <returns></returns>
        public static bool IsInRole(string role) {
            foreach (var item in User.Roles) {
                if (string.Compare(role, item.Code, true) == 0)
                    return true;
            }
            return false;
        }

        /// <summary>
        /// 判断用户是否属于一个组织
        /// </summary>
        /// <param name="orgId">组织Id</param>
        /// <returns></returns>
        public static bool IsInOrg(int orgId) {
            return User.Organizations.Where(c => c.Id == orgId &&
                (c.Flag & OrganizationUsed.Assigned) == OrganizationUsed.Assigned).Count() > 0;
        }

        /// <summary>
        /// 判断以一个用户是否可以操作该组织
        /// </summary>
        /// <param name="orgId">组织Id</param>
        /// <returns></returns>
        public static bool CanPossessOrg(int orgId) {
            return User.Organizations.Where(c => c.Id == orgId &&
               (c.Flag & OrganizationUsed.Processed) == OrganizationUsed.Processed).Count() > 0;
        }

        /// <summary>
        /// 获取用户的组织Id串
        /// </summary>
        /// <returns></returns>
        public static string[] GetOrgIds() {
            return User.Organizations.Select(c => c.Id.ToString()).ToArray();
        }

        ///// <summary>
        ///// 构建用户角色信息
        ///// </summary>
        ///// <param name="user"></param>
        ///// <param name="createPersistent"></param>
        ///// <returns></returns>
        private static IPrincipal CreatePrincial(User user, bool createPersistent) {
            if ((user as IIdentity).IsAuthenticated) {
                _tokenProvider.CreateToken(user, createPersistent);
                User = user;
                return CreatePrincipal(user.Identity);
            }
            return null;
        }

        /// <summary>
        /// 创建用户线程上下文凭据
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static IPrincipal CreatePrincipal(string name) {
            return new GenericPrincipal(new GenericIdentity(name, string.Empty), null);
        }

        internal static ITokenProvider GetTokenProvider() {
            return Container.Current.Resolve<ITokenProvider>();
        }

        internal static ICacheStorage GetCacheStorage(ITokenProvider tokenProvider) {
            ICacheStorage item;
            if (Container.Current.IsRegistered<ICacheStorage>())
                item = Container.Current.Resolve<ICacheStorage>();
            else
                item = new DefatultCacheStorage();

            var val = Container.Current.BuildUp(item.GetType(), item) as ICacheStorage;
            var storage = val as DefatultCacheStorage;
            if (storage != null)
                Debug.Assert(storage.TokenProvider != null);
            Trace.TraceInformation("Authorization\tresolve cache storage");
            return val;
        }
    }
}
